// gfx_OpenGL.c

#include "gfx_opengl.h"

  
/* This expects "src" to be width*height*3 bytes in size
   This expects "dst" to be width*height*4 bytes in size
   src is in RGB format (you can edit it for BGR if you want)
   dst is in RGBA format afterwards */

typedef unsigned char my_byte;

void fish(my_byte *dst, const my_byte *src, unsigned int width, unsigned int height) {
  unsigned int a, size = width*height;

  for(a=0; a<size; ++a) {
    *(dst++) = src[0]; /* R */
    *(dst++) = src[1]; /* G */
    *(dst++) = src[2]; /* B */
    if(src[0] == 0xFF && src[1] == 0x00 && src[2] == 0xFF)
      *(dst++) = 0x00;
    else
      *(dst++) = 0xFF;
    src += 3;
  }
}

void inputCallbackUp( unsigned char key, int x, int y )
{
	switch( key )
	{
		case 'a':
			inputManager.KeyUp( inputManager.cControllerA );
		break;

		case 'b':
			inputManager.KeyUp( inputManager.cControllerB );
		break;

		case 0x1B: case 'q':
			inputManager.KeyUp( inputManager.cControllerBack );
		break;

		case 0x0D: //Enter / Return
			inputManager.KeyDown( inputManager.cControllerStart );
		break;

		default:	
		break;
	}
}

void inputCallbackDown( unsigned char key, int x, int y )
{
	switch( key )
	{
		case 'a':
			inputManager.KeyDown( inputManager.cControllerA );
		break;

		case 'b':
			inputManager.KeyDown( inputManager.cControllerB );
		break;

		case 0x1B: case 'q': // Escape or q
			inputManager.KeyDown( inputManager.cControllerBack );
		break;

		case 0x0D: //Enter / Return
			inputManager.KeyDown( inputManager.cControllerStart );
		break;

		default:
			//printf("%i\n", key );
		break;
	}
}

void inputCallbackSpecialUp( int key, int x, int y )
{
	switch( key )
	{
		case GLUT_KEY_UP:
			inputManager.KeyUp( inputManager.cControllerUp );
		break;

		case GLUT_KEY_DOWN:
			inputManager.KeyUp( inputManager.cControllerDown );
		break;

		case GLUT_KEY_LEFT:
			inputManager.KeyUp( inputManager.cControllerLeft );
		break;

		case GLUT_KEY_RIGHT:
			inputManager.KeyUp( inputManager.cControllerRight );
		break;

		default:
		break;
	}
}

void inputCallbackSpecialDown( int key, int x, int y )
{
	switch( key )
	{
		case GLUT_KEY_UP:
			inputManager.KeyDown( inputManager.cControllerUp );
		break;

		case GLUT_KEY_DOWN:
			inputManager.KeyDown( inputManager.cControllerDown );
		break;

		case GLUT_KEY_LEFT:
			inputManager.KeyDown( inputManager.cControllerLeft );
		break;

		case GLUT_KEY_RIGHT:
			inputManager.KeyDown( inputManager.cControllerRight );
		break;

		default:
		break;
	}
}

namespace Skitchin_OpenGL
{
	int CDrawingInterface::blit( CAbstractDrawable *drawable )
	{
		if( drawable->GetU( ) >= drawable->GetTXW( ) || drawable->GetV( ) >= drawable->GetTXH( ) )
			return -1;

		float x1 = drawable->GetX( );
		float y1 = drawable->GetY( );
		float x2 = (float)(drawable->GetX( ) + drawable->GetW( ));
		float y2 = (float)(drawable->GetY( ) + drawable->GetH( ));

		GLuint *thetexture = (GLuint*)drawable->GetTexturePointer( );

		float u1 = (float)( drawable->GetU( ) ) / (float)drawable->GetTXW( );
		float v1 = (float)( drawable->GetTXH( ) - drawable->GetV( ) ) / (float)drawable->GetTXH( );
		float u2 = u1 + ( (float)( drawable->GetW( ) ) / (float)drawable->GetTXW( ) );
		float v2 = v1 - ( (float)( drawable->GetH( ) ) / (float)drawable->GetTXH( ) );

		glEnable( GL_TEXTURE_2D );
		glBindTexture( GL_TEXTURE_2D, *thetexture );

		glBegin( GL_QUADS );
			if( drawable->GetHFlip( ) == false && drawable->GetVFlip( ) == false )
			{
			
				glTexCoord2f (u1, v1);
				glVertex2f( x1, y1 );
				glTexCoord2f( u2, v1 );
				glVertex2f( x2, y1 );
				glTexCoord2f( u2, v2 );
				glVertex2f( x2, y2 );
				glTexCoord2f( u1, v2 );
				glVertex2f( x1, y2 );
			}
			else if( drawable->GetHFlip( ) == true && drawable->GetVFlip( ) == false )
			{
				glTexCoord2f (u2, v1);
				glVertex2f( x1, y1 );
				glTexCoord2f( u1, v1 );
				glVertex2f( x2, y1 );
				glTexCoord2f( u1, v2 );
				glVertex2f( x2, y2 );
				glTexCoord2f( u2, v2 );
				glVertex2f( x1, y2 );
			}
			else if( drawable->GetHFlip( ) == false && drawable->GetVFlip( ) == true )
			{
				glTexCoord2f (u1, v2);
				glVertex2f( x1, y1 );
				glTexCoord2f( u2, v2 );
				glVertex2f( x2, y1 );
				glTexCoord2f( u2, v1 );
				glVertex2f( x2, y2 );
				glTexCoord2f( u1, v1 );
				glVertex2f( x1, y2 );
			}
			else if( drawable->GetHFlip( ) == true && drawable->GetVFlip( ) == true )
			{
				glTexCoord2f (u2, v2);
				glVertex2f( x1, y1 );
				glTexCoord2f( u1, v2 );
				glVertex2f( x2, y1 );
				glTexCoord2f( u1, v1 );
				glVertex2f( x2, y2 );
				glTexCoord2f( u2, v1 );
				glVertex2f( x1, y2 );
			}
		glEnd( );


		return 0;
	};

    void CDrawingInterface::destroywindow( )
	{
		glutHideWindow( );
		glutDestroyWindow( glutGetWindow( ) );
	};

    void CDrawingInterface::fillrect( SRGB color, SRect rectarea )
	{
		glColor3i( color.r, color.g, color.b );
		glRecti( (int)rectarea.x, (int)rectarea.y, rectarea.w, rectarea.h );
	};

    void CDrawingInterface::fillscreen( SRGB color )
	{
		glClearColor( color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, 0.0f );
		glClear( GL_COLOR_BUFFER_BIT );
	};

    void CDrawingInterface::initwindow( unsigned int w, unsigned int h, unsigned int bpp, const char * title )
	{
		this->rctScreen.w = w;
		this->rctScreen.h = h;
		this->Bpp = bpp;
		glutInit( 0, 0 );
		glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA );
		glutInitWindowSize( w, h );
		glutCreateWindow( title );
		glMatrixMode( GL_PROJECTION );
		glLoadIdentity( );
		glOrtho( 0, w, h, 0, 0, 1 );
		glMatrixMode( GL_MODELVIEW );
		glDisable( GL_DEPTH_TEST );
		glTranslatef( 0.375, 0.375, 0 );
		glEnable( GL_TEXTURE_2D );
		glBlendFunc( GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA );
		glEnable( GL_BLEND );
		glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );

		glutKeyboardFunc( inputCallbackDown );
		glutKeyboardUpFunc( inputCallbackUp );
		glutSpecialFunc( inputCallbackSpecialDown );
		glutSpecialUpFunc( inputCallbackSpecialUp );
	};

    void CDrawingInterface::swapbuffers( )
	{
		glutSwapBuffers();
	};

	int CDrawingInterface::Tick( )
	{
		this->CalcFPS( );

		if ( iFPSLimit > 0 )
			this->ThrottleFPS( );

		glutPostRedisplay( );
		glutMainLoopEvent( );

		this->swapbuffers( );

		return 0;
	};

	int CDrawable::TextureFromFile( char * file, SRGB alphacolor )
	{
		GLuint * texture = new GLuint;
		GLuint * texture2 = new GLuint;
		HBITMAP hBMP;	// Handle Of The Bitmap
		BITMAP	BMP;	// Bitmap Structure

		char * path = new char[256];

		strcpy( path, "assets/" );
		strcat( path, file );

		hBMP = ( HBITMAP )LoadImage( GetModuleHandle(NULL), path, IMAGE_BITMAP, 0,
								   0, LR_CREATEDIBSECTION | LR_LOADFROMFILE );
 
		if ( hBMP != NULL )
		{
			GetObject( hBMP, sizeof( BMP ), &BMP );


			this->SetW( BMP.bmWidth );
			this->SetH( BMP.bmHeight );
			this->SetTXW( BMP.bmWidth );
			this->SetTXH( BMP.bmHeight );

			glGenTextures( 1, texture );
			glGenTextures( 1, texture2 );

			glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );	// Pixel Storage Mode (Word Alignment / 4 Bytes)
			glBindTexture( GL_TEXTURE_2D, *texture );
			

			glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
			glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

			glTexImage2D( GL_TEXTURE_2D, 0, 3, BMP.bmWidth, BMP.bmHeight, 0,
						  GL_BGR_EXT, GL_UNSIGNED_BYTE, BMP.bmBits );

			DeleteObject( hBMP );
			this->texturedata = texture;
			this->texturefilename = new char[ strlen( path ) ];
			strcpy( this->texturefilename, path );
			return 0;
		}// Only errors proceed beyond this point.

		printf( "ERROR loading file: %s!\n", path );
    	    
		return -1; // error
		
		return 0;
	};
};

